﻿using BCCommon;
using BCCommon.Enums;
using BCCommon.Extensions;
using BCCommon.PaymentEnums;
using BCData.Common.Payment.AliPayBill;
using BCData.Common.Payment.Payment;
using BCData.Common.Payment.WxPayBill;
using BCData.Common.Wallet;
using BCDto.Common.FlowRecord;
using BCDto.Common.Payment.Payment;
using BCDto.Common.Payment.PaymentRecord;
using BCDto.Common.SystemFinanceFlowRecord;
using BCDto.Sim.Admin.Admin;
using BCEntity.Common.Payment.Payment;
using BCService.Common.FlowRecord;
using BCService.Common.Payment.Payment;
using BCService.Common.SystemFinanceFlowRecord;
using BCService.Utils;
using System;
using System.Collections.Generic;
using System.Linq;

namespace BCService.Common.Payment.PaymentRecord
{
    /// <summary>
    /// 支付记录服务
    /// </summary>
    public class CommonPaymentRecordService : ICommonPaymentRecordService
    {
        private readonly IPaymentService paymentService;
        private readonly IPaymentRecordData paymentRecordData;
        private readonly IWxPayBillData wxPayBillData;
        private readonly IAliPayBillData aliPayBillData;
        //private readonly IPaymentReceiptCertificateService paymentReceiptCertificateService;
        private readonly IWalletData walletData;
        private readonly IUserFlowRecordService userFlowRecordService;
        private readonly ISystemFinanceFlowRecordService systemFinanceFlowRecordService;

        public CommonPaymentRecordService(IPaymentService paymentService,
            IPaymentRecordData paymentRecordData,
            IWxPayBillData wxPayBillData,
            IAliPayBillData aliPayBillData,
            //IPaymentReceiptCertificateService paymentReceiptCertificateService,
            IWalletData walletData,
            IUserFlowRecordService userFlowRecordService,
            ISystemFinanceFlowRecordService systemFinanceFlowRecordService)
        {
            this.paymentService = paymentService;
            this.paymentRecordData = paymentRecordData;
            this.wxPayBillData = wxPayBillData;
            this.aliPayBillData = aliPayBillData;
            //this.paymentReceiptCertificateService = paymentReceiptCertificateService;
            this.walletData = walletData;
            this.userFlowRecordService = userFlowRecordService;
            this.systemFinanceFlowRecordService = systemFinanceFlowRecordService;
        }

        /// <summary>
        /// 获取支付记录信息
        /// </summary>
        /// <param name="paymentRecordId">支付记录Id</param>
        /// <returns></returns>
        public PaymentRecordDto GetPaymentRecord(long paymentRecordId)
        {
            return this.paymentRecordData.GetPaymentRecordByPaymentRecordId(paymentRecordId).As<PaymentRecordDto>();
        }

        public PageableList<PaymentRecordDto> GetPaymentRecordsByUserId(int pageIndex, int pageSize, long userId)
        {
            var list = this.paymentRecordData.GetPaymentRecordsByUserId(pageIndex, pageSize, out int dataCount, userId);
            return new PageableList<PaymentRecordDto>
            {
                Count = dataCount,
                Items = list.Select(p => p.As<PaymentRecordDto>())
            };
        }

        public PaymentRecordDto UpdatePaymentRecordStatusSuccess(long paymentRecordId)
        {
            PaymentRecordEntity entity = null;
            while (entity == null)
            {
                entity = this.paymentRecordData.SetAsSuccess(paymentRecordId);
            }
            return entity.As<PaymentRecordDto>();
        }

        /// <summary>
        /// 修改支付记录验签时间
        /// </summary>
        /// <param name="paymentRecordId">支付记录Id</param>
        /// <returns></returns>
        public bool UpodatePaymentRecordCheckSignTime(long paymentRecordId)
        {

            PaymentRecordEntity entity = null;
            while (entity == null)
            {
                entity = this.paymentRecordData.SetAsSignTime(paymentRecordId);
            }
            return true;
        }

        public PageableList<PaymentRecordUserViewDto> Query(string userName, string phone, SourceAppType? appType, FlowType? orderType, PaymentChannelType? payChannelType, DateTime? start, DateTime? end, int? pageIndex, int? pageSize, PaymentResultType? paymentStatus = PaymentResultType.Success)
        {
            var list = paymentRecordData.Query(userName, phone, appType, orderType, payChannelType, start, end, pageIndex, pageSize, paymentStatus).GetAwaiter().GetResult();

            return new PageableList<PaymentRecordUserViewDto>
            {
                Items = list.Item1.As<IEnumerable<PaymentRecordUserViewDto>>(),
                Count = list.Item2,
                PageIndex = pageIndex.HasValue ? pageIndex.Value : 1,
                PageSize = pageSize.HasValue ? pageSize.Value : 10,
                ServerTime = DateTime.Now
            };
        }

        public decimal Refund(AdminDto adminDto, RefundRequestDto refundRequestDto)
        {
            long paymentRecordId = refundRequestDto.PaymentRecordId;
            decimal refundAmount = refundRequestDto.RefundAmount;
           
            if (paymentRecordId == 0)
            {
                throw new ArgumentException("付款记录Id不能为空");
            }
            
            if (refundAmount == 0)
            {
                throw new ArgumentException("退款金额应大于0");
            }

            PaymentRecordEntity entity = this.paymentRecordData.GetPaymentRecordByPaymentRecordId(paymentRecordId);

            if (entity == null)
            {
                throw new ArgumentException("此支付记录不存在");
            }

            IChannelFactoryService channelFactoryService = this.paymentService.GetChannelService(entity.AppType.AsEnum<SourceAppType>(), entity.PaymentChannelId, entity.OSType.AsEnum<SourceOSType>());

            return channelFactoryService.Refund(adminDto.AdminId, adminDto.FullName, paymentRecordId, refundAmount);
        }

        public PaymentRecordBillViewDto GetBillDetails(string vehicleServiceNotificationOrderCode)
        {
            var entity = this.paymentRecordData.GetSuccessByReferenceNumberAsync(vehicleServiceNotificationOrderCode).GetAwaiter().GetResult();
            if (entity != null)
            {
                if (entity.PaymentChannelId == PaymentChannelType.Alipay)
                {
                    var bill = this.aliPayBillData.GetEntityByPaymentRecordId(entity.PaymentRecordId).GetAwaiter().GetResult();
                    if (bill != null)
                    {
                        entity.TradeDate = bill.TradeDate;
                        entity.TradeNo = bill.AliPayOrderNo;
                        entity.HandlingFee = bill.HandlingFee;
                        entity.ActualReceiveAmount = bill.ActualReceiveAmount;
                        entity.IsSettle = bill.IsSettle;
                    }
                }
                else if (entity.PaymentChannelId == PaymentChannelType.Weixin)
                {
                    var bill = this.wxPayBillData.GetEntityByPaymentRecordId(entity.PaymentRecordId).GetAwaiter().GetResult();
                    if (bill != null)
                    {
                        entity.TradeDate = bill.TradeDate;
                        entity.TradeNo = bill.WxOrderNo;
                        entity.HandlingFee = bill.HandlingFee;
                        entity.ActualReceiveAmount = bill.ActualReceiveAmount;
                        entity.IsSettle = bill.IsSettle;
                    }
                }
            }
            return entity.As<PaymentRecordBillViewDto>();
        }

        public bool InsertRefundRecord(PaymentRecordDto paymentRecordDto)
        {
            var paymentRecordEntity = this.paymentRecordData.GetSuccessByReferenceNumberAsync(paymentRecordDto.ReferenceNumber).GetAwaiter().GetResult();
            
            if (paymentRecordEntity.OrderType == FlowType.Recharge)
            {
                if (paymentRecordEntity.IsRefund.HasValue && paymentRecordEntity.IsRefund.Value)
                {
                    return true;
                }
            
                var rechargeRecord = this.paymentRecordData.GetNoRefundSuccessByReferenceNumberAsync(paymentRecordDto.ReferenceNumber).GetAwaiter().GetResult();

                decimal refundAmount = Math.Abs(paymentRecordEntity.Amount);
                var walletEntity = this.walletData.GetWalletByUserId(paymentRecordEntity.UserId.Value);
                
                if (walletEntity != null && walletEntity.Balance >= refundAmount)
                {
                    this.userFlowRecordService.AddAsync(new UserFlowRecordDto()
                    {
                        Channel = paymentRecordEntity.PaymentChannelId,
                        Amount = paymentRecordEntity.Amount,
                        Balance = walletEntity.Balance - refundAmount,
                        ChannelDesc = paymentRecordEntity.PaymentChannelId.GetDescriptionString(),
                        FlowDirection = SourceFlowDirectionType.Outlay,
                        FlowDirectionDesc = SourceFlowDirectionType.Outlay.GetDescriptionString(),
                        FlowType = FlowType.Refund,
                        FlowTypeDesc = FlowType.Refund.GetDescriptionString(),
                        OrderNo = string.Empty,
                        Remark = "原路退回",
                        UserId = paymentRecordEntity.UserId.Value,
                        UserName = paymentRecordEntity.UserName
                    });
                    walletEntity.Balance = walletEntity.Balance - refundAmount;
                    SystemFinanceFlowRecordDto systemFinanceFlowRecordDto = new SystemFinanceFlowRecordDto
                    {
                        Amount = paymentRecordEntity.Amount,
                        Remark = $"{FlowType.Refund.GetDescriptionString()}: {string.Format("{0:N2}", (paymentRecordEntity.Amount))}元",
                        FlowDirection = SourceFlowDirectionType.Outlay,
                        FlowType = FlowType.Refund,
                        Channel = paymentRecordEntity.PaymentChannelId,
                        UserId = paymentRecordEntity.UserId.Value,
                        UserName = paymentRecordEntity.UserName
                    };
                    this.systemFinanceFlowRecordService.Append(systemFinanceFlowRecordDto);

                    decimal serviceFee = Math.Round(rechargeRecord.Amount / 100 * 0.3M, 2, MidpointRounding.AwayFromZero);
                    decimal actualServiceAmount = serviceFee;
                    if (rechargeRecord.Amount >= refundAmount)
                    {
                        if (walletEntity.Balance - serviceFee < 0)
                        {
                            decimal serviceDifferenceAmount = Math.Abs(walletEntity.Balance - serviceFee);
                            actualServiceAmount = serviceFee - serviceDifferenceAmount;
                        }
                        this.userFlowRecordService.AddAsync(new UserFlowRecordDto()
                        {
                            Channel = paymentRecordEntity.PaymentChannelId,
                            Amount = -actualServiceAmount,
                            Balance = walletEntity.Balance - actualServiceAmount,
                            ChannelDesc = paymentRecordEntity.PaymentChannelId.GetDescriptionString(),
                            FlowDirection = SourceFlowDirectionType.Outlay,
                            FlowDirectionDesc = SourceFlowDirectionType.Outlay.GetDescriptionString(),
                            FlowType = FlowType.ServiceCharge,
                            FlowTypeDesc = FlowType.ServiceCharge.GetDescriptionString(),
                            OrderNo = string.Empty,
                            Remark = "手续费",
                            UserId = paymentRecordEntity.UserId.Value,
                            UserName = paymentRecordEntity.UserName
                        });
                        walletEntity.Balance = walletEntity.Balance - actualServiceAmount;
                    }
                    this.walletData.Update(walletEntity);
                    systemFinanceFlowRecordDto = new SystemFinanceFlowRecordDto
                    {
                        Amount = actualServiceAmount,
                        Remark = $"{FlowType.Refund.GetDescriptionString()}手续费: {string.Format("{0:N2}", (actualServiceAmount))}元",
                        FlowDirection = SourceFlowDirectionType.Income,
                        FlowType = FlowType.ServiceCharge,
                        Channel = PaymentChannelType.Balance,
                        UserId = paymentRecordEntity.UserId.Value,
                        UserName = paymentRecordEntity.UserName
                    };
                    this.systemFinanceFlowRecordService.Append(systemFinanceFlowRecordDto);
                }
                var result = this.paymentRecordData.UpdateRefundSuccess(paymentRecordEntity.PaymentRecordId).GetAwaiter().GetResult();
                result = this.paymentRecordData.UpdateRefundSuccess(rechargeRecord.PaymentRecordId).GetAwaiter().GetResult();
                return result != null;
            }
            return false;
            //else
            //{
            //    FMISCapitalSourceType sourceType = paymentRecordDto.PaymentChannelId.ToString() == "Weixin" ? FMISCapitalSourceType.Wechat : FMISCapitalSourceType.Alipay;
            //    var result = this.paymentReceiptCertificateService.AddSpecificRecord(sourceType, paymentRecordDto.ReferenceNumber);
            //    return result;
            //}
        }

        public PaymentRecordDto GetRefundPaymentRecord(string referenceNumber)
        {
            return this.paymentRecordData.GetRefundPaymentRecord(referenceNumber).GetAwaiter().GetResult().As<PaymentRecordDto>();
        }

        public IEnumerable<PaymentRecordDto> GetEntityByReferenceNumberAsync(string referenceNumber)
        {
            return this.paymentRecordData.GetEntityByReferenceNumberAsync(referenceNumber).GetAwaiter().GetResult().As<IEnumerable<PaymentRecordDto>>();
        }

        public PageableList<PaymentRecordDto> GetPaymentRecordsByAgentRecordId(int pageIndex, int pageSize, long agentRecordId)
        {
            var list = this.paymentRecordData.GetPaymentRecordsByAgentRecordId(pageIndex, pageSize, out int dataCount, agentRecordId);
            return new PageableList<PaymentRecordDto>
            {
                Count = dataCount,
                Items = list.Select(p => p.As<PaymentRecordDto>())
            };
        }
    }
}
